package com.boot.filter;

import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.boot.common.constant.MessageConstant;
import com.boot.common.constant.RedisConstant;
import com.boot.common.property.SecurityProperties;
import com.boot.common.result.Result;
import com.boot.modules.auth.service.CustomUserDetailsService;
import com.boot.util.JwtUtil;
import com.boot.util.RedisUtil;
import jakarta.annotation.Nonnull;
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
import org.springframework.web.filter.OncePerRequestFilter;

import java.io.IOException;
import java.io.PrintWriter;
import java.util.Arrays;
import java.util.List;


/**
 * JWT令牌过滤器
 */
@Slf4j
@RequiredArgsConstructor
public class JwtAuthFilter extends OncePerRequestFilter {

    private final JwtUtil jwtUtil;

    private final RedisUtil redisUtil;

    private final CustomUserDetailsService customUserDetailsService;

    private final SecurityProperties securityProperties;

    @Override
    protected void doFilterInternal(@Nonnull HttpServletRequest request, @Nonnull HttpServletResponse response, @Nonnull FilterChain filterChain)
            throws ServletException, IOException {
        //过滤白名单
        List<String> unAuthUris = Arrays.asList(securityProperties.getIgnoreUrls());
        String uri = request.getRequestURI();
        if (!unAuthUris.contains(uri)) {
            //获取渠道
            String channel = request.getHeader("Channel");
            //获取授权代码
            String authorizationHeader = request.getHeader("Authorization");
            if (authorizationHeader != null && authorizationHeader.startsWith("Bearer ")) {
                //截取令牌
                String token = authorizationHeader.substring(7);
                //验证JWT令牌是否有效
                if (jwtUtil.validateToken(token)) {
                    String username = jwtUtil.getUsernameFromToken(token);
                    //检查Redis中是否存在该令牌
                    String tokenKey = StrUtil.format(RedisConstant.Auth.TOKEN, channel, username);
                    if (redisUtil.hasKey(tokenKey) &&
                            token.equals(redisUtil.get(tokenKey))) {
                        //获取用户信息
                        UserDetails userDetails = customUserDetailsService.loadUserByUsername(username);
                        if (userDetails == null){
                            responseError(response, 401, MessageConstant.USERNAME_NOT_EXIST);
                            return;
                        }
                        UsernamePasswordAuthenticationToken authenticationToken =
                                new UsernamePasswordAuthenticationToken(
                                        userDetails, null, userDetails.getAuthorities());
                        authenticationToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(request));
                        SecurityContextHolder.getContext().setAuthentication(authenticationToken);
                        //验证码接口权限

                    } else {
                        responseError(response, 401, MessageConstant.TOKEN_EXPIRE);
                        return;
                    }
                } else {
                    responseError(response, 401, MessageConstant.TOKEN_EXPIRE);
                    return;
                }
            } else {
                responseError(response, 401, MessageConstant.TOKEN_EXPIRE);
                return;
            }
        }
        filterChain.doFilter(request, response);
    }

    /**
     * 响应错误信息
     *
     * @param response 响应
     * @param message  错误信息
     */
    private void responseError(HttpServletResponse response, int code, String message) {
        response.setContentType("application/json;charset=UTF-8");
        try (PrintWriter writer = response.getWriter()) {
            writer.print(JSONUtil.toJsonStr(Result.error(code, message)));
            writer.flush();
        } catch (IOException e) {
            log.error("响应异常处理失败", e);
        }
    }
}